package com.qisu.command;

import java.util.Date;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.qisu.cacheMap.ChargeQuantity;
import com.qisu.cacheMap.OrderIdCache;
import com.qisu.cacheMap.TimeAndPriceCache;
import com.qisu.converters.Converters;
import com.qisu.enums.ACorDC;
import com.qisu.enums.ChargeMethod;
import com.qisu.enums.GunConnectState;
import com.qisu.enums.PileState;
import com.qisu.enums.ShowState;
import com.qisu.enums.WorkingState;
import com.qisu.model.packet.ChargeStatusPacket;
import com.qisu.model.vo.Gun;
import com.qisu.model.vo.Pile;
import com.qisu.service.GunService;
import com.qisu.service.OrderService;
import com.qisu.service.PileService;

import cn.hutool.core.date.DateUtil;
import io.netty.channel.ChannelHandlerContext;

/**
 * 信息包
 * 
 * @author li
 * 
 */
@Component
public class CMD104 extends AbstractSinexcelCmd {
	@Autowired
	private GunService gunService;
	@Autowired
	private OrderService orderServeice;

	@Autowired
	private OrderIdCache orderIdCache;
	@Autowired
	private ChargeQuantity chargeQuantity;
	@Autowired
	private TimeAndPriceCache timeAndPriceCache;
	@Autowired
	private PileService pileService;

	@PostConstruct
	@Override
	protected void init() {
		super.init();

		fieldsList.addField(new CommandFieldDef<Integer>(2)).addField(new CommandFieldDef<Integer>(2))
				.addField(new CommandFieldDef<String>(32, Converters.strConverter, "充电桩ID"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "充电枪数量"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "充电口号"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "充电枪类型"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "工作状态"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "当前SOC%"))
				.addField(new CommandFieldDef<Integer>(4, Converters.seIntConverter, "当前最高告警编码"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "车连接状态"))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "本次充电累计充电费"))
				.addField(new CommandFieldDef<Integer>(4)).addField(new CommandFieldDef<Integer>(4))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "直流充电电压"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "直流充电电流"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "bms需求电压"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "bms需求电流"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "bms充电模式"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流A相充电电压"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流B相充电电压"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流C相充电电压"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流A相充电电流"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流B相充电电流"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "交流C相充电电流"))
				.addField(new CommandFieldDef<Integer>(2, Converters.intConverter, "剩余充电时间"))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "充电时长"))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "本次充电累计充电电量"))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "充电前电表读数"))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "当前电表读数"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "充电启动方式"))
				.addField(new CommandFieldDef<Byte>(1, Converters.byteConverter, "充电策备"))
				.addField(new CommandFieldDef<Integer>(4, Converters.seIntConverter, "充电策略参数"))
				.addField(new CommandFieldDef<Byte>(1/* , Converters.byteConverter, "预约标志" */))
				.addField(new CommandFieldDef<String>(32/* , Converters.strConverter, "充电/预约卡号" */))
				.addField(new CommandFieldDef<Byte>(1/* , Converters.byteConverter, "预约超时时间" */))
				.addField(new CommandFieldDef<String>(8, Converters.hexConverter, "预约/开始充电开始时间"))
				.addField(new CommandFieldDef<Integer>(4/* , Converters.intConverter, "充电前卡余额" */))
				.addField(new CommandFieldDef<Integer>(4))
				.addField(new CommandFieldDef<Integer>(4, Converters.intConverter, "充电功率"));
	}

	@Override
	protected void processCmd(ChannelHandlerContext ctx, byte[] req) {
		String pileSerial = fieldsList.getValue("充电桩ID");
		int gunNo = ((Byte) fieldsList.getValue("充电口号")).intValue();
		int connStatus = ((Byte) fieldsList.getValue("车连接状态")).intValue();
		int workStatus = ((Byte) fieldsList.getValue("工作状态")).intValue();
		int chargeQualityInDegree = fieldsList.getValue("本次充电累计充电电量");
		int startType = ((Byte) fieldsList.getValue("充电启动方式")).intValue();
		int soc = ((Byte) fieldsList.getValue("当前SOC%")).intValue();
		Gun gun = new Gun();
		gun.setPileSerial(pileSerial);
		gun.setGunNo(gunNo);

		Pile pile = new Pile();
		pile.setPileSerial(pileSerial);
		if (GunConnectState.Connected.getValue() == connStatus) {
			if (WorkingState.Standby.getValue() == workStatus) {
				gun.setShowStatus(ShowState.Connected.getValue());
				pile.setPileState(PileState.Connected.getValue());
			} else {
				gun.setShowStatus(StatusMap.showStatus[workStatus].getValue());
				pile.setPileState(StatusMap.pileStatus[workStatus].getValue());
			}
		} else {
			if (WorkingState.Fault.getValue() == workStatus) {
				gun.setShowStatus(ShowState.Failure.getValue());
				pile.setPileState(PileState.Failure.getValue());
			} else {
				gun.setShowStatus(ShowState.Available.getValue());
				pile.setPileState(PileState.Available.getValue());
			}
		}
		gunService.updateGunState(gun);
		pileService.updatePileState(pile);
		if (startType != 0) {
			if (WorkingState.Charging.getValue() == workStatus && GunConnectState.Connected.getValue() == connStatus) {
				if (null != chargeQuantity.get(pileSerial + gunNo)) {
					int increase = chargeQualityInDegree - chargeQuantity.get(pileSerial + gunNo);
					if (increase > 0) { // 有电量说明不是第一次上报
						chargeQuantity.put(pileSerial + gunNo, chargeQualityInDegree); // 将本次总电量更新进缓存
						ChargeStatusPacket packet = getChargeStatusPacket(pileSerial, gunNo, chargeQualityInDegree,
								increase);
						orderServeice.updateOrderInfo(packet);
					}
					if (!orderServeice.hasSufficientFund(orderIdCache.get(pileSerial + gunNo))) {
						stopCharge(pileSerial, gunNo);
						log.warn(" ===== {}:{}余额不足,停止充电  =====", pileSerial, gunNo);
					}
					// 当车充满时下发停止指令
					if (soc == 100 && orderServeice.hasSufficientFund(orderIdCache.get(pileSerial + gunNo))) {
						stopCharge(pileSerial, gunNo);
						log.warn(" ===== {}:{}车辆充满,停止充电  =====", pileSerial, gunNo);
					}
				} else if (null == chargeQuantity.get(pileSerial + gunNo) && chargeQualityInDegree > 0) {
					stopCharge(pileSerial, gunNo);
					log.warn(" ===== {}:{}电量上报异常,停止充电  =====", pileSerial, gunNo);
				} else {
					log.info("  === 启动充电 第一次上报 ===");
					// 查询上次(重启前)订单电量与资费id,和当前时间段的价格.
					orderServeice.getCurrentPrice(pileSerial, gunNo);
				}
			}
		}

		responseClient(ctx);
	}

	private ChargeStatusPacket getChargeStatusPacket(String pileSerial, int gunNo, int chargeQualityInDegree,
			int increase) {

		int nowTime = Integer.parseInt(DateUtil.format(new Date(), "HHmm"));
		int chargedTimeInSecond = fieldsList.getValue("充电时长");

		return new ChargeStatusPacket() {
			{
				if (nowTime != 0 && nowTime < (timeAndPriceCache.get(pileSerial + gunNo).getDivisionEndTime())) {
					setChargePrice(timeAndPriceCache.get(pileSerial + gunNo).getChargePrice());
					servicePrice(timeAndPriceCache.get(pileSerial + gunNo).getServicePrice());
				} else {
					orderServeice.getCurrentPrice(pileSerial, gunNo);
					setChargePrice(timeAndPriceCache.get(pileSerial + gunNo).getChargePrice());
					servicePrice(timeAndPriceCache.get(pileSerial + gunNo).getServicePrice());
				}

				// setPileSerial(pileSerial);
				// setGunNo(gunNo);
				setOrderId(orderIdCache.get(pileSerial + gunNo));
				setIncreaseQuantity(increase); // 本次增长电量
				setChargedQualityInDegree(chargeQualityInDegree);
				String startChargeTime = fieldsList.getValue("预约/开始充电开始时间");
				setStartChargeTime(startChargeTime.substring(0, startChargeTime.length() - 2));
				setAccumulatedMeterReading(fieldsList.getValue("当前电表读数"));
				setPileChargeMode(ChargeMethod.AutoUntilFull);
				setChargedTimeInSecond(chargedTimeInSecond);
				// setPaymentRequiredInCent(fieldsList.getValue("本次充电累计充电费"));
				setChargingRemain(fieldsList.getValue("剩余充电时间"));

				if (ACorDC.AC.getValue() == ((Byte) fieldsList.getValue("充电枪类型")).intValue()) {
					int phaseAVoltage = fieldsList.getValue("交流A相充电电压");
					int phaseBVoltage = fieldsList.getValue("交流B相充电电压");
					int phaseCVoltage = fieldsList.getValue("交流C相充电电压");
					int phaseAAmpere = fieldsList.getValue("交流A相充电电流");
					int phaseBAmpere = fieldsList.getValue("交流B相充电电流");
					int phaseCAmpere = fieldsList.getValue("交流C相充电电流");
					setInputCurrentVoltage(phaseAVoltage);
					setInputCurrentAmpere(phaseAAmpere * 10);
					setDcOutputVoltage(0);
					setDcOutputAmpere(0);
				} else {
					int currentVoltage = fieldsList.getValue("直流充电电压");
					int currentAmpere = fieldsList.getValue("直流充电电流");
					setInputCurrentVoltage(0);
					setInputCurrentAmpere(0);
					setDcOutputVoltage(currentVoltage);
					setDcOutputAmpere(currentAmpere * 10);
				}

				// setGunChargeMode(StatusMap.chargeMode[((Byte)_fieldsList.getValue("bms充电模式")).intValue()]);
				setBmsVoltageRequired(fieldsList.getValue("bms需求电压"));
				setBmsAmpereRequired(fieldsList.getValue("bms需求电流"));
				setCurrSOC(((Byte) fieldsList.getValue("当前SOC%")).intValue());
			}
		};
	}

	private void stopCharge(String pileSerial, int gunNo) {
		// cmd=5
		// @formatter:off
		byte[] p = new byte[] { (byte) 0xAA, (byte) 0xF5, // signature
				(byte) 0x19, (byte) 0x00, // length
				(byte) 0x10, // version
				(byte) 0x00, // sequence
				(byte) 0x05, (byte) 0x00, // command

				(byte) 0x00, (byte) 0x00, // preserved1
				(byte) 0x00, (byte) 0x00, // preserved2

				(byte) gunNo, // gun no
				(byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, // subcommand
				(byte) 0x01, // no. of parameters
				(byte) 0x04, (byte) 0x00, // parameters length
				(byte) 0x55, (byte) 0x00, (byte) 0x00, (byte) 0x00, // param
				(byte) 0x00 // checksum
		};
		// @formatter:on

		ChannelHandlerContext ctx = channelList.get(pileSerial);
		send(ctx, p);
	}

	@Override
	protected void responseClient(ChannelHandlerContext ctx) {
		// cmd=103
		// @formatter:off
		byte[] p = new byte[] { (byte) 0xAA, (byte) 0xF5, // signature
				(byte) 0x0D, (byte) 0x00, // length
				(byte) 0x10, // version
				(byte) 0x00, // sequence
				(byte) 0x67, (byte) 0x00, // command
				(byte) 0x00, (byte) 0x00, // preserved1
				(byte) 0x00, (byte) 0x00, // preserved2
				(byte) 0x00 // checksum
		};
		// @formatter:on

		setCommand(p, 103);
		send(ctx, p);
	}

}
